package com.sky.service.impl;

import com.alibaba.fastjson.JSON;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.sky.annotation.MyLog;
import com.sky.constant.MessageConstant;
import com.sky.constant.RedisConstant;
import com.sky.constant.StatusConstant;
import com.sky.context.BaseContext;
import com.sky.dto.EmployeeDTO;
import com.sky.dto.EmployeeLoginDTO;
import com.sky.dto.EmployeePageQueryDTO;
import com.sky.entity.Employee;
import com.sky.exception.AccountLockedException;
import com.sky.exception.AccountNotFoundException;
import com.sky.exception.LoginFailedException;
import com.sky.exception.PasswordErrorException;
import com.sky.mapper.EmployeeMapper;
import com.sky.result.PageResult;
import com.sky.result.Result;
import com.sky.service.EmployeeService;
import org.apache.commons.lang.BooleanUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;

import javax.annotation.Resource;
import java.lang.reflect.Method;
import java.time.LocalDateTime;
import java.util.List;
import java.util.concurrent.TimeUnit;

@Service
public class EmployeeServiceImpl implements EmployeeService {

    @Autowired
    private EmployeeMapper employeeMapper;

    @Resource
    private StringRedisTemplate stringRedisTemplate;



    /**
     * 员工登录
     *
     * @param employeeLoginDTO
     * @return
     */
    //开启记录时间 利用aop注解 记录数据到数据库中
    public Employee login(EmployeeLoginDTO employeeLoginDTO) {
        String username = employeeLoginDTO.getUsername();
        String password = employeeLoginDTO.getPassword();

        //1、根据用户名查询数据库中的数据
        Employee employee = employeeMapper.getByUsername(username);
        if (employee.getStatus()==0){
            throw new LoginFailedException("账号已被禁用 请重试");
        }

        //2、处理各种异常情况（用户名不存在、密码不对、账号被锁定）
        if (employee == null) {
            //账号不存在
            throw new AccountNotFoundException(MessageConstant.ACCOUNT_NOT_FOUND);
        }

        //密码比对
        //我自行进行了破解 获取到了密码
        String passwordMD5 = DigestUtils.md5DigestAsHex(password.getBytes());
        password = DigestUtils.md5DigestAsHex(password.getBytes());
        if (!password.equals(employee.getPassword())) {
            //密码错误
            throw new PasswordErrorException(MessageConstant.PASSWORD_ERROR);
        }

        if (employee.getStatus() == StatusConstant.DISABLE) {
            //账号被锁定
            throw new AccountLockedException(MessageConstant.ACCOUNT_LOCKED);
        }

        //3、返回实体对象
        return employee;
    }

    /**
     * 进行添加操作
     * @param employeeDTO
     * @return
     * @Transactional 开启事务 @Propagation 传播行为默认
     * 不论是否有事务，都创建新事务，运行在一个独立的事务中
     */
    //开启事务
//    @Transactional(propagation = Propagation.REQUIRES_NEW)
    @MyLog
    @Override
    public Result insertemployee(EmployeeDTO employeeDTO) {
        Employee employee = new Employee();
        //将Dto中的属性 全部赋值给 entity对象
        BeanUtils.copyProperties(employeeDTO,employee);


        //需求1 判断是否传递参数为空 为空则直接返回错误信息
        if (StringUtils.isBlank(employee.toString())){
            //如果为空 则直接返回错误
        return Result.error("用户信息错误");
        }
        //需求2 如果不为空则 查询数据库中是否有这个人
        Boolean result=employeeMapper.selectByname(employee.getName());
        if (result!=null){
            return Result.error("用户名不存在 请去注册");
        }
//        int a=1/0;

        //需求3 进行添加 将用户添加到数据库中
        employee.setCreateTime(LocalDateTime.now());
        employee.setUpdateTime(LocalDateTime.now());
        employee.setPassword("123456");
        String MD5password = DigestUtils.md5DigestAsHex(employee.getPassword().getBytes());
        employee.setPassword(MD5password);

        //通过threadLocal拿去添加人的id
        //获取到本地id
        Long currentId = BaseContext.getCurrentId();
        employee.setCreateUser(currentId);
        employee.setUpdateUser(currentId);

        employeeMapper.insert(employee);
        //添加成功后 要去删除 redis缓存.
        stringRedisTemplate.delete(RedisConstant.CACHE_EMPLOYEE);

        return Result.success();

    }

    /**
     * 进行优化 使用redis 进行缓存操作
     * @param employeePageQueryDTO
     * @return
     */
    @Override
    @MyLog
    public PageResult selectByPage(EmployeePageQueryDTO employeePageQueryDTO) {
        PageResult pageResult = SetNXmethod(employeePageQueryDTO);
        return pageResult;
    }

    public PageResult SetNXmethod(EmployeePageQueryDTO employeePageQueryDTO){
        //解决缓存 穿透
        //如果查询null值 直接 返回null
        String RedisNull = stringRedisTemplate.opsForValue().get(RedisConstant.CACHE_NULL);
        if (StringUtils.isNotBlank(RedisNull)){
            PageResult pageResult = JSON.parseObject(RedisNull, PageResult.class);
            return pageResult;
        }

        //使用缓存 存数据数据
        String cacthemp = stringRedisTemplate.opsForValue().get(RedisConstant.CACHE_EMPLOYEE);
        if (StringUtils.isNotBlank(cacthemp)){
            //如果不为空 从缓存查出来的数据直接返回给前端
            PageResult pageResult = JSON.parseObject(cacthemp, PageResult.class);
            return pageResult;
        }


        //在这里要获取互斥锁
        PageResult pageResult = null;
        try {
            Boolean aBoolean = setNX(RedisConstant.LOCK_SETNX);
            if (aBoolean==false){
                //如果等于失败 则证明 有人正在重建 数据
                Thread.sleep(1000);
                return selectByPage(employeePageQueryDTO);
            }

            //调用分页插件 进行自动limit 操作
            PageHelper.startPage(employeePageQueryDTO.getPage(),employeePageQueryDTO.getPageSize());
            //调用查询 根据名字进行like 模糊查询
            Page<Employee> page=employeeMapper.selectByPagename(employeePageQueryDTO.getName());
            //总数量

            //解决缓存 穿透问题。 他给了我一个为空的玩意
            if (page==null){
                stringRedisTemplate.opsForValue().set(RedisConstant.CACHE_NULL,null,2,TimeUnit.MINUTES);
            }


            long total = page.getTotal();
            List<Employee> records = page.getResult();
            //获取分页结果
            pageResult = new PageResult(total, records);

            //将查询的结果缓存到reids中
            String Redisresults = JSON.toJSONString(pageResult);
            //设置为空 解决缓存击穿问题
            if (StringUtils.isBlank(Redisresults)){
                stringRedisTemplate.opsForValue().set(RedisConstant.CACHE_NULL,null);
            }
            //添加到redis中 设置一个超时剔除 已保证redis不会一直存在这人

            stringRedisTemplate.opsForValue().set(RedisConstant.CACHE_EMPLOYEE,Redisresults,30, TimeUnit.MINUTES );
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            deleteBySetNX(RedisConstant.LOCK_SETNX);
        }

        return pageResult;
    }



    //解决缓存穿透 设置互斥锁
    public Boolean setNX(String key){
        //设置互斥锁 解决 缓存击穿
        Boolean aBoolean = stringRedisTemplate.opsForValue().setIfAbsent(key, "1", 1, TimeUnit.SECONDS);
        boolean aTrue = BooleanUtils.isTrue(aBoolean);
        //如果为true  则为设置成功 返回数据
        return aTrue;
    }
    public void deleteBySetNX(String key){
        stringRedisTemplate.delete(key);
    }


    @Override
    public Result putstatus(Integer status, Integer id) {
        //修改员工状态 正常状态为0  禁用状态为1
        //我要根据id 修改这个人的 状态
        employeeMapper.putstatus(status,id);
        //修改状态后 要删除redis数据
        stringRedisTemplate.delete(RedisConstant.CACHE_EMPLOYEE);
        return Result.success();
    }

    @Override
    public Employee selectByid(Integer id) {
        //没必要设置这个缓存
        Employee employees = employeeMapper.selectById(id);

        return employees;
    }

    @Override
    public Result putList(EmployeeDTO employeeDTO) {
        //修改数据
        //进行增删改时 要将数据 删除 解决数据一致性问题
        employeeMapper.putList(employeeDTO);
        stringRedisTemplate.delete(RedisConstant.CACHE_EMPLOYEE);
        return Result.success();
    }

}






























